home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1999 June
/
Macworld (1999-06).dmg
/
Shareware World
/
Info
/
For Developers
/
MacZoop2.0.sea
/
MacZoop2.0
/
Required Classes
/
CursorUtilities.cpp
< prev
next >
Wrap
Text File
|
1999-01-30
|
14KB
|
540 lines
/*************************************************************************************************
*
*
* CursorUtilities.c - cursor stuff- spinning watch, etc
*
* 26/9/94 ©1994, Graham Cox
*
* This code based on "Macintosh Programming Secrets" 2nd Edition by Keith Rollin & Scott K
*
*************************************************************************************************/
#include "CursorUtilities.h"
#include <LowMem.h>
#include <QDOffscreen.h>
// globals:
static short gVBLCount;
static VBLTaskWithA5Ptr gCursorTask = NULL;
static acurHdl gWatchSpinner = NULL;
static acurHdl gBallSpinner = NULL;
static acurHdl gBusyArrowSpinner = NULL;
static CCrsrHandle gColourCursor = NULL;
static Boolean gCursorPaused = FALSE;
static long gPauseTicks;
static Point gMouseTrack;
short gLastCursorID = -1;
// macro for doing dodgy 68000 stuff!
#ifndef __POWERPC__
VBLTaskWithA5Ptr GetVBLRec(void) = 0x2008;
#endif
static Handle GetDetachedResource( ResType aType, short anID );
/*-------------------------*** GETDETACHEDRESOURCE ***--------------------------------*/
/*
returns a detched resourse with type and ID passed.
----------------------------------------------------------------------------------------*/
static Handle GetDetachedResource( ResType aType, short anID )
{
Handle temp = NULL;
temp = GetResource( aType, anID );
if ( temp )
DetachResource( temp );
return temp;
}
/*--------------------------*** INITANIMATEDCURSOR ***--------------------------------*/
/*
sets up an animated cursor by reading the 'acur' resource with the ID passed.
----------------------------------------------------------------------------------------*/
acurHdl InitAnimatedCursor( short resID )
{
acurHdl temp;
short ccount;
CursHandle aCursor;
CursHandle* workPtr;
temp = (acurHdl) GetDetachedResource( 'acur', resID );
if (temp)
{
HNoPurge((Handle) temp );
ccount = (*temp)->numCursors;
(*temp)->numCursors *= 32;
(*temp)->index = 0;
HLock((Handle) temp);
workPtr = (*temp)->cursors;
while( ccount-- )
{
aCursor = (CursHandle) GetDetachedResource( 'CURS', *(short*) workPtr );
HNoPurge((Handle) aCursor );
*workPtr++ = aCursor;
}
HUnlock((Handle) temp );
}
return temp;
}
/*------------------------*** DISPOSEANIMATEDCURSOR ***-------------------------------*/
/*
disposes of an animated cursor
----------------------------------------------------------------------------------------*/
void DisposeAnimatedCursor( acurHdl cursH )
{
short ccount, mc;
mc = (*cursH)->numCursors / 32;
for( ccount = 0; ccount < mc; ccount++ )
DisposeHandle((Handle) (*cursH)->cursors[ ccount ] );
DisposeHandle((Handle) cursH );
}
/*-------------------------*** STARTCURSORANIMATION ***-------------------------------*/
/*
sets up the VBL queue to animate the cursor passed.
----------------------------------------------------------------------------------------*/
void StartCursorAnimation( short period, acurHdl theCursor )
{
// primes the VBL queue to spin the cursor automatically
VBLUPP vTaskUPP;
if (theCursor)
{
LockCursorData(theCursor);
gVBLCount = period;
gCursorTask = (VBLTaskWithA5Ptr) NewPtr( sizeof( VBLTaskWithA5 ));
gCursorTask->theTask.qType = vType;
vTaskUPP = NewVBLProc((ProcPtr) VBLCursorSpin );
gCursorTask->theTask.vblAddr = vTaskUPP;
gCursorTask->theTask.vblCount = period;
gCursorTask->theTask.vblPhase = 0;
gCursorTask->A5 = (long) LMGetCurrentA5();
gCursorTask->theCursor = theCursor;
(void) VInstall((QElemPtr) gCursorTask );
gCursorPaused = FALSE;
}
}
/*-------------------------*** STOPCURSORANIMATION ***--------------------------------*/
/*
Kills the current cursor in the VBL queue and unlocks it. This then restores the arrow.
----------------------------------------------------------------------------------------*/
void StopCursorAnimation()
{
// removes spinning cursor task from the VBL queue
acurHdl theCursor;
if ( gCursorTask )
{
(void) VRemove((QElemPtr) gCursorTask );
theCursor = gCursorTask->theCursor;
DisposeRoutineDescriptor( gCursorTask->theTask.vblAddr );
DisposePtr((Ptr) gCursorTask);
gCursorTask = NULL;
UnlockCursorData( theCursor );
// force switch to default cursor:
gLastCursorID = -1;
SetCursorShape( 0 );
}
}
/*------------------------*** PAUSECURSORANIMATION ***--------------------------------*/
/*
Pauses the current animating cursor, returning the cursor to the shape passed. If the
cursor is not animating, this does nothing. The animation can be restarted with a call
to ResumeCursorAnimation().
----------------------------------------------------------------------------------------*/
void PauseCursorAnimation( short tempFixedCursorID )
{
if ( CursorAnimating())
{
gCursorPaused = TRUE;
gLastCursorID = -1;
gPauseTicks = 0;
}
SetCursorShape( tempFixedCursorID );
}
/*------------------------*** RESUMECURSORANIMATION ***-------------------------------*/
/*
Restarts a paused animating cursor, if there is one, otherwise does nothing but reset the
flag.
----------------------------------------------------------------------------------------*/
void ResumeCursorAnimation()
{
gCursorPaused = FALSE;
}
/*-----------------------------*** ANIMATECURSOR ***----------------------------------*/
/*
Can be called repeatedly to animate a cursor. However, it is simpler to let the VBL task
do this in general.
----------------------------------------------------------------------------------------*/
void AnimateCursor( short increment, acurHdl theCursor )
{
// used only for non-VBL cursor spinning
short oldIndex,newIndex;
CursHandle aCursor;
if ( theCursor && !gCursorPaused )
{
oldIndex = (*theCursor)->index / 32;
(*theCursor)->index += increment;
(*theCursor)->index %= (*theCursor)->numCursors;
newIndex = (*theCursor)->index / 32;
if (newIndex != oldIndex)
{
aCursor = (*theCursor)->cursors[newIndex];
SetCursor(*aCursor);
}
}
}
/*----------------------------*** LOCKCURSORDATA ***----------------------------------*/
/*
locks down the cursor handles ready for animation
----------------------------------------------------------------------------------------*/
void LockCursorData( acurHdl theCursor )
{
// locks the cursor and its internal data
short ccount;
CursHandle *workPtr;
ccount = (*theCursor)->numCursors / 32;
HLockHi((Handle) theCursor);
workPtr = (*theCursor)->cursors;
while (ccount--)
HLockHi((Handle) *workPtr++);
}
/*--------------------------*** UNLOCKCURSORDATA ***----------------------------------*/
/*
unlocks the cursor handles
----------------------------------------------------------------------------------------*/
void UnlockCursorData( acurHdl theCursor )
{
// unlocks the cursor
short ccount;
CursHandle *workPtr;
ccount = (*theCursor)->numCursors / 32;
workPtr = (*theCursor)->cursors;
while (ccount--)
HUnlock((Handle) *workPtr++);
HUnlock((Handle) theCursor);
}
/*-----------------------------*** VBLCURSORSPIN ***----------------------------------*/
/*
this is the VBL callback that actually performs the animation of a VBL-driven cursor
----------------------------------------------------------------------------------------*/
#ifndef __POWERPC__
void VBLCursorSpin()
#else
void VBLCursorSpin(VBLTaskWithA5Ptr theTask)
#endif
{
// this is the function that is called from the interrupt task.
acurHdl theCrsr;
long oldA5;
#ifndef __POWERPC__
VBLTaskWithA5Ptr theTask;
theTask = GetVBLRec();
oldA5 = SetA5(theTask->A5);
if (LMGetCrsrBusy() == 0)
{
theCrsr = theTask->theCursor;
AnimateCursor( 32, theCrsr );
}
theTask->theTask.vblCount = gVBLCount;
(void) SetA5( oldA5 );
#else
oldA5 = SetA5( theTask->A5 );
if (LMGetCrsrBusy() == 0)
{
theCrsr = theTask->theCursor;
AnimateCursor( 32, theCrsr );
}
theTask->theTask.vblCount = gVBLCount;
(void) SetA5( oldA5 );
#endif
}
/*-----------------------------*** SETWATCHCURSOR ***---------------------------------*/
/*
starts the animated watch cursor. To stop it, call StopCursorAnimation.
----------------------------------------------------------------------------------------*/
void SetWatchCursor()
{
// HL call installs VBL spinner for watch, which should have been inited beforehand
if ( gCursorTask )
StopCursorAnimation();
if ( gWatchSpinner && ! gCursorTask )
StartCursorAnimation( 8, gWatchSpinner );
}
/*---------------------------*** SETBEACHBALLCURSOR ***-------------------------------*/
/*
starts the animated "beachball" cursor. To stop it, call StopCursorAnimation.
----------------------------------------------------------------------------------------*/
void SetBeachBallCursor()
{
if ( gCursorTask )
StopCursorAnimation();
if ( gBallSpinner && ! gCursorTask )
StartCursorAnimation( 3, gBallSpinner );
}
void SetBusyArrowCursor()
{
if ( gCursorTask )
StopCursorAnimation();
if ( gBallSpinner && ! gCursorTask )
StartCursorAnimation( 3, gBusyArrowSpinner );
}
/*-------------------------------*** GETMODIFIERS ***---------------------------------*/
/*
can be called at any time to get the current state of the keyboard modifier keys. Useful
if the cursor is changed with different modifiers down.
----------------------------------------------------------------------------------------*/
short GetModifiers()
{
// returns the current state of the modifier keys as if it was the modifier field in
// an event record. This can be called at any time, even when an event is not available.
// It is very useful for updating the cursor to show the modifier state.
short modifiers = 0;
unsigned char theKeys[16];
KeyMap* pKeys;
pKeys = (KeyMap*) theKeys;
GetKeys( *pKeys );
((theKeys[0x37 >> 3] >> (0x37 & 7)) & 1)? modifiers |= cmdKey : 0;
((theKeys[0x38 >> 3] >> (0x38 & 7)) & 1)? modifiers |= shiftKey : 0;
((theKeys[0x39 >> 3] >> (0x39 & 7)) & 1)? modifiers |= alphaLock : 0;
((theKeys[0x3A >> 3] >> (0x3A & 7)) & 1)? modifiers |= optionKey : 0;
((theKeys[0x3B >> 3] >> (0x3B & 7)) & 1)? modifiers |= controlKey : 0;
(! Button())? modifiers |= btnState : 0;
return modifiers;
}
/*------------------------------*** APPCURSORINIT ***---------------------------------*/
/*
high-level call to initialise the animated cursors. Extend this for others you may have
----------------------------------------------------------------------------------------*/
void AppCursorInit()
{
// called once at application start-up to initialise the animated cursor globals.
gWatchSpinner = InitAnimatedCursor( kWatchResID );
gBallSpinner = InitAnimatedCursor( kBeachBallResID );
gBusyArrowSpinner = InitAnimatedCursor( kBusyArrowResID );
gLastCursorID = -1;
gColourCursor = NULL;
}
/*------------------------------*** APPCURSORFREE ***---------------------------------*/
/*
high-level call to delete all animated cursors
----------------------------------------------------------------------------------------*/
void AppCursorFree()
{
StopCursorAnimation();
DisposeAnimatedCursor( gWatchSpinner );
DisposeAnimatedCursor( gBallSpinner );
DisposeAnimatedCursor( gBusyArrowSpinner );
}
/*------------------------------*** CURSORANIMATING ***-------------------------------*/
/*
returns TRUE if an animated cursor is on screen. You should not call
SetCursor if this is true, or ugly cursor flickering occurs.
----------------------------------------------------------------------------------------*/
Boolean CursorAnimating()
{
return (( gCursorTask != NULL ) && !gCursorPaused );
}
Boolean CursorAnimationVBLRunning()
{
return ( gCursorTask != NULL );
}
/*-------------------------------*** SETCURSORSHAPE ***-------------------------------*/
/*
You can use this instead of GetCursor/SetCursor to set the cursor shape. This also will
correctly manage a colour cursor if there is one available with the same ID. This just
works without any special coding, optimising for loading the colour cursor minimally.
If you pass 0 it will set the arrow. This method is unsuitable for VBL animated cursors.
----------------------------------------------------------------------------------------*/
void SetCursorShape( short resID )
{
if (( resID != gLastCursorID ) && ! CursorAnimating())
{
CCrsrHandle ncH = NULL;
gLastCursorID = resID;
if ( resID == 0 )
SetCursor( &qd.arrow );
else
{
// see if there's a colour cursor with that ID. We do not look for colour
// cursors with IDs less than 128 since for some reason Inside Mac says
// this is not allowed. Can't really see why this is!
if ( resID > 127 )
{
ncH = GetCCursor( resID );
if ( ncH )
{
// due to a subtle and undocumented side-effect of SetCCursor, we must be
// careful to preserve the GDevice around this call, or bad things can
// happen.
GDHandle saveDevice;
saveDevice = GetGDevice();
SetCCursor( ncH );
SetGDevice( saveDevice );
}
}
// if no colour cursor, use a black and white one. This works with all
// cursor IDs, including the standard iBeamCursor, crossCursor, etc.
if ( ncH == NULL )
{
CursHandle cH;
cH = GetCursor( resID );
if ( cH )
{
HLockHi((Handle) cH );
SetCursor( *cH );
HUnlock((Handle) cH );
}
}
}
// dispose any old colour cursor
if ( gColourCursor )
DisposeCCursor( gColourCursor );
// keep track of new one or NULL
gColourCursor = ncH;
}
}